#include "stdafx.h"
#include "cvxImage.h"
#include <iostream>
#include "cvxSf.h"
#include "cvxVideo.h"

using std::cerr;
using std::cout;
using std::endl;

void CvxVideo::showVideo(const vector<IplImage*>& imageVec, int interval)
{
	cvNamedWindow("showImage", 1);
	vector<IplImage*>::const_iterator iI = imageVec.begin();
	for (; iI != imageVec.end(); ++iI)
	{
		cvShowImage("showImage", *iI);
		cvWaitKey(interval);
	}
	cvDestroyWindow("showImage");
}

bool CvxVideo::horizontalCombineAVI(char* fileNameA, char* fileNameB, int interval)
{
	//check parameters
	assert(fileNameA);
	assert(fileNameB);
	//grab images from video
	vector<IplImage*> imageVecA;
	vector<IplImage*> imageVecB;		
	if(!CvxVideo::getImageFromAVI(fileNameA, imageVecA) || !CvxVideo::getImageFromAVI(fileNameB, imageVecB))
	{
		cerr<<"horizontalCombineAVI load avi fail!"<<endl;
		return false;
	}
	//combine video
	if (imageVecA.size() != imageVecB.size())
	{
		cerr<<"CvxSF::horizontalCombineVideo: "<<endl;
		cerr<<"imageVecA.size() = "<<imageVecA.size()<<endl;
		cerr<<"imageVecB.size() = "<<imageVecB.size()<<endl;
		getchar();
	}
	vector<IplImage*> combinedImage;
	for (vector<IplImage*>::iterator iA = imageVecA.begin(), 
		iB = imageVecB.begin(); iA != imageVecA.end() && iB != imageVecB.end();
		++iA, ++iB)
	{
		int widthA = (*iA)->width;
		int heightA = (*iA)->height;

		int widthB = (*iB)->width;
		int heightB = (*iB)->height;

		int totalWidth = widthA + widthB + interval;
		int totalHeight = max(heightA, heightB);

		IplImage* largeImg = cvCreateImage(cvSize(totalWidth, totalHeight), IPL_DEPTH_8U, 3);
		//	cvSet(largeImg, cvScalarAll(255));
		cvZero(largeImg);

		cvSetImageROI(largeImg, cvRect(0, 0, widthA, heightA));
		cvCopy(*iA, largeImg, NULL);
		cvResetImageROI(largeImg);

		cvSetImageROI(largeImg, cvRect(widthA+interval, 0, widthB, heightB));
		cvCopy(*iB, largeImg, NULL);
		cvResetImageROI(largeImg);
		combinedImage.push_back(largeImg);

	}
	//save image sequences
	CvxSF::saveImageVec(combinedImage, "combined_img//");
	//release images
	CvxSF::releaseImage(imageVecA);
	CvxSF::releaseImage(imageVecB);
	CvxSF::releaseImage(combinedImage);
}

 bool CvxVideo::horizontalCombineAVI(string fileNameA, string fileNameB, string saveFile, float scale, int interval)
 {
	
	 assert(scale < 1.0);
	 CvCapture *cpA = cvCaptureFromFile(fileNameA.c_str());
	 if (!cpA)
	 {
		 printf("can not open file %s\n", fileNameA.c_str());
		 return false;
	 }

	 CvCapture *cpB = cvCaptureFromFile(fileNameB.c_str());
	 if (!cpB)
	 {
		 printf("can not open file %s\n", fileNameB.c_str());
		 return false;
	 }		
	 IplImage *frameA = cvQueryFrame(cpA);
	 IplImage *frameB = cvQueryFrame(cpB);
	 assert(frameA);
	 assert(frameB);

	 int widthA = frameA->width;
	 int heightA = frameA->height;

	 int widthB = frameB->width;
	 int heightB = frameB->height;

	 int totalWidth = widthA + widthB + interval;
	 int totalHeight = std::max(heightA, heightB);

	 IplImage *largeImg = cvCreateImage(cvSize(totalWidth, totalHeight), IPL_DEPTH_8U, 3);
	 assert(largeImg);
	 IplImage *scaleImg = cvCreateImage(cvSize(cvRound(totalWidth * scale), cvRound(totalHeight * scale)), IPL_DEPTH_8U, 3);
	 assert(scaleImg);
	 CvVideoWriter *writer = cvCreateAVIWriter(saveFile.c_str(), CV_FOURCC('M','J','P','G'), 24, cvSize(scaleImg->width, scaleImg->height), 1); 
	 if (!writer)
	 {
		 cerr<<"can nor create AVI writer!"<<endl;
		 return false;
	 }

	 while(frameA && frameB)
	 {
		 cvSetImageROI(largeImg, cvRect(0, 0, widthA, heightA));
		 cvCopy(frameA, largeImg, NULL);
		 cvResetImageROI(largeImg);

		 cvSetImageROI(largeImg, cvRect(widthA+interval, 0, widthB, heightB));
		 cvCopy(frameB, largeImg, NULL);
		 cvResetImageROI(largeImg);

		 cvResize(largeImg, scaleImg);
		 cvWriteToAVI(writer, scaleImg);

		 frameA = cvQueryFrame(cpA);
		 frameB = cvQueryFrame(cpB);
	 }
	 cvReleaseCapture(&cpA);
	 cvReleaseCapture(&cpB);
	 cvReleaseImage(&largeImg);	
	 cvReleaseImage(&scaleImg);
	 cvReleaseVideoWriter(&writer);	
	 return true;
 }

bool CvxVideo::getImageFromAVI(char* fileName, vector<IplImage*>& outImg, int maxFrame)
{
	assert(fileName);
	assert(outImg.empty());
	string t_fileName = string(fileName);
	//check file name
	string::size_type idex = t_fileName.find('.');
	if (idex != string::npos)
	{
		string suffix = t_fileName.substr(idex);
		if (suffix != ".avi"){ 
			cerr<<"CvxSF::getImageFromAVI error 1 "<<endl;
			return false;
		}
	}
	else{
		cerr<<"CvxSF::getImageFromAVI error 2 "<<endl;
		return false;
	}
	//capture images
	CvCapture* capture = cvCaptureFromAVI(t_fileName.c_str());
	if (!capture){
		cerr<<"CvxSF::getImageFromAVI error 3 "<<endl;
		return false;
	}
	IplImage* pFrame = NULL;
	while ((pFrame = cvQueryFrame(capture)) && maxFrame--)
	{
		IplImage* storeImg = cvCreateImage(cvGetSize(pFrame), IPL_DEPTH_8U, 3);
		cvCopy(pFrame, storeImg, NULL);
		outImg.push_back(storeImg);
	}
	if (capture){
		cvReleaseCapture(&capture);
	}
}

bool CvxVideo::writeImageToAVI(const vector<IplImage *> &Imgs, const string fileName, double fps)
{
	if (Imgs.empty() || fileName.empty())
	{
		return false;
	}
	int w = Imgs[0]->width;
	int h = Imgs[0]->height;

	string f = fileName + string(".avi");

	CvVideoWriter *writer = cvCreateAVIWriter(f.c_str(), CV_FOURCC('M','J','P','G'), fps, cvSize(w, h), 1); 
	if (!writer)
	{
		cerr<<"can nor create AVI writer!"<<endl;
		return false;
	}
	for (int i = 0; i<Imgs.size(); ++i)
	{
		if (Imgs[i])
		{
			cvWriteToAVI(writer, Imgs[i]);
		}
	}
	cvReleaseVideoWriter(&writer);	
}

bool CvxVideo::writeToAVI(const string fromFileName, const string toFileName, double fps, const CvSize &size, 
						  int jumpFrameNumbers, int frameLength, int flipMode)
{
	CvCapture *cp = cvCaptureFromFile(fromFileName.c_str());
	if (!cp)
	{
		printf("can not open file %s\n", fromFileName.c_str());
		return false;
	}		
	while(jumpFrameNumbers--)
	{
		int isOk = cvGrabFrame(cp);
		if (isOk == 0)
		{
			break;
		}
	}
	if (jumpFrameNumbers != -1)
	{
		printf("no frames to record\n");
		return false;
	}
	IplImage *frame = cvQueryFrame(cp);
	int w = frame->width;
	int h = frame->height;
	IplImage *saveFrame = cvCreateImage(size, IPL_DEPTH_8U, 3);
	CvVideoWriter *writer = cvCreateAVIWriter(toFileName.c_str(), CV_FOURCC('M','J','P','G'), fps, size, 1); 
	if (!writer)
	{
		printf("can not create %s video writer\n", toFileName.c_str());
		return false;
	}
	while(frame && frameLength--)
	{
		if (w == size.width && h == size.height)
		{
			cvCopy(frame, saveFrame);
		}
		else
		{
			cvResize(frame, saveFrame);
		}
		if (flipMode == 1 || flipMode == 0)
		{
			cvFlip(saveFrame, saveFrame, flipMode);
		}
		cvWriteToAVI(writer, saveFrame);
		frame = cvQueryFrame(cp);
	}
	cvReleaseCapture(&cp);
	cvReleaseVideoWriter(&writer);
	cvReleaseImage(&saveFrame);
	return true;
}

bool CvxVideo::writeIdxToAVI(const string fromFileName, const string toFileName, 
							 double fps, const vector<int> &idx)
{
	assert(idx.size() > 0);

	CvCapture *cp = cvCaptureFromFile(fromFileName.c_str());
	if (!cp)
	{
		printf("can not open file %s\n", fromFileName.c_str());
		return false;
	}			
	IplImage *frame = cvQueryFrame(cp);
	int w = frame->width;
	int h = frame->height;	
	CvVideoWriter *writer = cvCreateAVIWriter(toFileName.c_str(), CV_FOURCC('M','J','P','G'), 
							fps, cvSize(w, h), 1); 
	if (!writer)
	{
		printf("can not create %s video writer\n", toFileName.c_str());
		return false;
	}
	// total number of video
	int count = cvGetCaptureProperty(cp, CV_CAP_PROP_FRAME_COUNT);
	for (int i = 0; i<idx.size(); ++i)
	{
		int frame_num = idx[i];
		if (frame_num >= 0 && frame_num < count)
		{
			int isOk = cvSetCaptureProperty(cp, CV_CAP_PROP_POS_FRAMES, frame_num);
			if (isOk)
			{
				IplImage *frame = cvQueryFrame(cp);
				assert(frame);
				cvWriteToAVI(writer, frame);					
			}
		}
	}	
	cvReleaseCapture(&cp);
	cvReleaseVideoWriter(&writer);
	return true;
}

bool CvxVideo::writeClearMarkToAVI(const string fromFileName, const string toFileName, 
								   double fps, const CvSize boardSize, float threshold)
{
	CvCapture *cp = cvCaptureFromFile(fromFileName.c_str());
	if (!cp)
	{
		printf("can not open file %s\n", fromFileName.c_str());
		return false;
	}			
	IplImage *frame = cvQueryFrame(cp);
	assert(frame);
	CvSize size = cvGetSize(frame);
	CvVideoWriter *writer = cvCreateAVIWriter(toFileName.c_str(), CV_FOURCC('M','J','P','G'), 
							fps, size, 1); 
	if (!writer)
	{
		printf("can not create %s video writer\n", toFileName.c_str());
		return false;
	}

	int count = cvGetCaptureProperty(cp, CV_CAP_PROP_FRAME_COUNT);
	if (count < 5)
	{
		return false;
	}

	vector<Point2f> prePts;
	vector<Point2f> curPts;
	vector<Point2f> nextPts;
	bool isPreOk = false;
	bool isCurOk = false;
	bool isNextOk = false;

	IplImage *curFrame = cvCreateImage(size, IPL_DEPTH_8U, 3);

	// 0
	isPreOk = CvxSF::GetChessboard(frame, false, boardSize, prePts);
	// 1
	frame = cvQueryFrame(cp);
	isCurOk = CvxSF::GetChessboard(frame, false, boardSize, curPts);
	cvCopy(frame, curFrame);
	// 2
	frame = cvQueryFrame(cp);
	isNextOk = CvxSF::GetChessboard(frame, false, boardSize, nextPts);

//	frame = cvQueryFrame(cp);
	while (frame)
	{
		if (isPreOk && isCurOk && isNextOk)
		{
			assert(prePts.size() == curPts.size() && curPts.size() == nextPts.size());
			float err_avg = 0.0;
			for (int j = 0; j<prePts.size(); ++j)
			{
				Point2f p1 = prePts[j];
				Point2f p2 = curPts[j];
				Point2f p3 = nextPts[j];
				err_avg += (p2.x - p1.x) * (p2.x - p1.x) +  (p2.y - p1.y) * (p2.y - p1.y);
				err_avg += (p2.x - p3.x) * (p2.x - p3.x) +  (p2.y - p3.y) * (p2.y - p3.y);
			}
			err_avg /= 2.0 * (boardSize.height * boardSize.width);
			printf("err_avg = %f\n", err_avg);
			if (err_avg < threshold)
			{
				cvWriteToAVI(writer, curFrame);
			//	cvShowImage("current frame", curFrame);
			//	cvWaitKey(0);

			}
		}
		//save next frame as current frame
		cvCopy(frame, curFrame);
		//get next frame
		frame = cvQueryFrame(cp);		
		if (frame)
		{
			prePts.clear();
			isPreOk = isCurOk;
			prePts = curPts;

			curPts.clear();
			isCurOk = isNextOk;
			curPts = nextPts;

			nextPts.clear();
			isNextOk = CvxSF::GetChessboard(frame, false, boardSize, nextPts);
		}
	}

	cvReleaseCapture(&cp);
	cvReleaseVideoWriter(&writer);
	cvReleaseImage(&curFrame);
	return true;
}

bool CvxVideo::divideVideo(const string fromFileName, const string toFileNamePrefix, 
				 vector<int> &stopIdx, double fps)
{
	assert(!stopIdx.empty());

	CvCapture *cp = cvCaptureFromFile(fromFileName.c_str());
	if (!cp)
	{
		printf("can not open file %s\n", fromFileName.c_str());
		return false;
	}
	int w = cvGetCaptureProperty(cp, CV_CAP_PROP_FRAME_WIDTH);
	int h = cvGetCaptureProperty(cp, CV_CAP_PROP_FRAME_HEIGHT);
	CvSize size = cvSize(w, h);

	IplImage *frame = NULL;

	for (int i = 0; i<stopIdx.size(); ++i)
	{
		int frameNum = 0;
		if (i == 0)
		{
			frameNum = stopIdx[i];
		}
		else
		{
			frameNum = stopIdx[i] - stopIdx[i-1];
		}

		assert(frameNum > 0);
		char buf[256] = {NULL};
		sprintf(buf, "%d", i-1);
		string fileName = toFileNamePrefix + string(buf) + string(".avi");
		CvVideoWriter *writer = cvCreateAVIWriter(fileName.c_str(), CV_FOURCC('M','J','P','G'), 
								fps, size, 1);
		if (!writer)
		{
			printf("can not create %s video writer\n", fileName.c_str());
			return false;
		}
		
		while(frameNum--)
		{
			frame = cvQueryFrame(cp);
			if (frame)
			{
				cvWriteToAVI(writer, frame);
			}			
		}
		cvReleaseVideoWriter(&writer);
	}
	cvReleaseCapture(&cp);
	return true;
}